package sample.magic_square_sol;

import java.util.*;

public class HelperFunctions
{
    static int[][] fixSquare;
 static int[] sumRow;
 static int[] sumColumn;
 static int leftDiagonal;
 static int rightDiagonal;
 static int time=0;
    public static int[][] makeInitialSquare(Random  rand, int n, boolean constrained,
        int     cRow,  int     cCol,int[][] square)
    {
fixSquare=new int[n][n];
        int nSquared     = n*n;
        int numPoolStart = 1;
        int numPoolSize  = nSquared;

        int numPoolIndex = 0;
        ArrayList<Integer> numPool = new ArrayList<Integer>(numPoolSize);

        for(int i=numPoolStart; i<=nSquared; i++)
        {
            numPool.add(numPoolIndex++, i);
        }
        // constrained problem removes 1->9 as valid numbers 
        // for initial square population
        ArrayList<Integer> fixPool = new ArrayList<Integer>(numPoolSize);



        if(constrained)
        {


            for (int i = 0; i <n ; i++) {
                for (int j = 0; j <n ; j++) {
                   if(square[i][j]!=0) {
                       fixPool.add(square[i][j]);
                       fixSquare[i][j] = 1;
                   }
                }
            }
Collections.sort(fixPool);

                while(!fixPool.isEmpty()){
                    int index=Collections.binarySearch(numPool,fixPool.get(0));
                    fixPool.remove(0);
                    numPool.remove(index);
                }

        }
        




        for(int i=0; i<n; i++)
        {
            for(int j=0; j<n; j++)
            {
                if(0 == square[i][j])
                {
                    int indx     = rand.nextInt(numPool.size());
                    square[i][j] = numPool.get(indx);
                    
                    numPool.remove(indx);
                }
            }
        }
        
        return square;
    }



    
    public static int evalFitness(int[][] square, int n, int m)
    {
        int fitness            = 0;
        
        int totalRowDiff       = 0;
        int totalColDiff       = 0;
        int leftToRightDiagSum = 0;
        int rightToLeftDiagSum = 0;
        
        for(int i=0; i<n; i++)
        {
            int rowSum = 0;
            int colSum = 0;
            
            for(int j=0; j<n; j++)
            {
                rowSum += square[i][j];
                colSum += square[j][i];
                
                if(i == j)
                {
                    leftToRightDiagSum += square[i][j];
                }
                
                if(i+j == n-1)
                {
                    rightToLeftDiagSum += square[i][j];
                }
            }
            
            totalRowDiff += Math.abs(rowSum-m);
            totalColDiff += Math.abs(colSum-m);
        }
                
        //System.out.println(totalRowDiff);
        //System.out.println(totalColDiff);
        
        // fitness = sum of all deviations
        // closer to 0 the better
        
        // fitness is based on degree of deviation in the sums of the rows,
        // columns & diagonals.
        fitness = Math.abs(leftToRightDiagSum-m) + Math.abs(rightToLeftDiagSum-m) + totalRowDiff + totalColDiff;
                
        return fitness;
    }


    public static int evalFitness(int[][] square, int n, int m,int oldFitness)
    {
        int fitness            = 0;

        int totalRowDiff       = 0;
        int totalColDiff       = 0;
        int leftToRightDiagSum = 0;
        int rightToLeftDiagSum = 0;

        for(int i=0; i<n; i++)
        {
            int rowSum = 0;
            int colSum = 0;

            for(int j=0; j<n; j++)
            {
                rowSum += square[i][j];
                colSum += square[j][i];

                if(i == j)
                {
                    leftToRightDiagSum += square[i][j];
                }

                if(i+j == n-1)
                {
                    rightToLeftDiagSum += square[i][j];
                }
            }

            totalRowDiff += Math.abs(rowSum-m);
            totalColDiff += Math.abs(colSum-m);
        }

        //System.out.println(totalRowDiff);
        //System.out.println(totalColDiff);

        // fitness = sum of all deviations
        // closer to 0 the better

        // fitness is based on degree of deviation in the sums of the rows,
        // columns & diagonals.

        fitness = Math.abs(leftToRightDiagSum-m) + Math.abs(rightToLeftDiagSum-m) + totalRowDiff + totalColDiff;

        return fitness;
    }

    public static int sum(Random rand, int[][] square, int n, int swapCount,int m,int oldFitness) {
    int result=oldFitness;
        int squareLength=square.length;
        int[][] copy = new int[square.length][square.length];
        for (int i = 0; i < squareLength; i++) {
            for (int j = 0; j < squareLength; j++) {
                copy[i][j]=square[i][j];
            }
        }

int r1 = 0,r2=0,c1=0,c2=0;
        int swapOne=-1;
        int swapTwo=-1;
        for(int i=0; i<swapCount; i++)
        {
            r1 = rand.nextInt(n);
             c1 = rand.nextInt(n);
             r2 = rand.nextInt(n);
             c2 = rand.nextInt(n);
            if(fixSquare[r1][c1]!=1&&fixSquare[r2][c2]!=1&&!(r1!=r2&&c2!=c2)) {
                swapOne=copy[r1][c1];
                swapTwo=copy[r2][c2];
                int temp = copy[r1][c1];
                copy[r1][c1] = copy[r2][c2];
                copy[r2][c2] = temp;
            }
            else{
                i--;
            }
        }
    if(time==0){
        int fitness            = 0;

        int totalRowDiff       = 0;
        int totalColDiff       = 0;
        int leftToRightDiagSum = 0;
        int rightToLeftDiagSum = 0;

        for(int i=0; i<n; i++)
        {
            int rowSum = 0;
            int colSum = 0;

            for(int j=0; j<n; j++)
            {
                rowSum += square[i][j];
                colSum += square[j][i];

                if(i == j)
                {
                    leftToRightDiagSum += square[i][j];
                }

                if(i+j == n-1)
                {
                    rightToLeftDiagSum += square[i][j];
                }
            }
          sumRow[i] = rowSum;
            sumColumn[i]=colSum;
            totalRowDiff += Math.abs(rowSum-m);
            totalColDiff += Math.abs(colSum-m);
        }

leftDiagonal=leftToRightDiagSum;
        rightDiagonal=rightToLeftDiagSum;
        fitness = Math.abs(leftToRightDiagSum-m) +
                Math.abs(rightToLeftDiagSum-m) + totalRowDiff + totalColDiff;

        result=fitness;
    }
    else{
        int fitness=oldFitness;
        int difference=swapOne-swapTwo;


       int distanceOne=Math.abs(sumRow[r1]-m);
        int distanceTwo=Math.abs(sumRow[r1]-difference-m);

        fitness-=(distanceOne-distanceTwo);

         distanceOne=Math.abs(sumRow[r2]-m);
         distanceTwo=Math.abs(sumRow[r2]-difference-m);
        fitness-=(distanceOne-distanceTwo);

        distanceOne=Math.abs(sumColumn[c1]-m);
        distanceTwo=Math.abs(sumColumn[c1]-difference-m);
        fitness-=(distanceOne-distanceTwo);

        distanceOne=Math.abs(sumColumn[c2]-m);
        distanceTwo=Math.abs(sumColumn[c2]-difference-m);
        fitness-=(distanceOne-distanceTwo);

        if(r1==c1){
            distanceOne=Math.abs(leftDiagonal-m);
            distanceTwo=Math.abs(leftDiagonal-difference-m);
            fitness-=(distanceOne-distanceTwo);
        }
        if(r2==c2){
            distanceOne=Math.abs(leftDiagonal-m);
            distanceTwo=Math.abs(leftDiagonal-difference-m);
            fitness-=(distanceOne-distanceTwo);
        }

        if(r1+c1==n){
            distanceOne=Math.abs(rightDiagonal-m);
            distanceTwo=Math.abs(rightDiagonal-difference-m);
            fitness-=(distanceOne-distanceTwo);
        }

        result=fitness;
    }


       time++;
    return result;
    }



    public static void outputSquare(int[][] square, int n)
    {
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<n; j++)
            {
                System.out.print(square[i][j] + " ");
            }
            
            System.out.println();
        }
    }    

    



    
    public static int[][] mutateSwapValues(Random rand, int[][] square, int n, int swapCount)
    {
        int squareLength=square.length;
        int[][] copy = new int[square.length][square.length];
        for (int i = 0; i < squareLength; i++) {
            for (int j = 0; j < squareLength; j++) {
                copy[i][j]=square[i][j];
            }
        }


        for(int i=0; i<swapCount; i++)
        {
            int r1 = rand.nextInt(n);
            int c1 = rand.nextInt(n);
            int r2 = rand.nextInt(n);
            int c2 = rand.nextInt(n);
            if(fixSquare[r1][c1]!=1&&fixSquare[r2][c2]!=1&&!(r1!=r2&&c2!=c2)) {
                int temp = copy[r1][c1];
                copy[r1][c1] = copy[r2][c2];
                copy[r2][c2] = temp;
            }
            else{
                i--;
            }
        }
        
        return copy;
    }
    
    

}
